package com.cwt.common.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

/**
 * ============= 【动态数据源】 ===============
 * >> Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源，这样我们可以在执行查询之前，设置使用的数据源。
 * >> druid 数据源配置切换: https://blog.csdn.net/swadian2008/article/details/103439112
 * >> 启动类添加 exclude = { DataSourceAutoConfiguration.class }，禁用数据源默认自动配置
 *
 * >> 【从库切换】 需要使用从库时候，在方法上添加DataSource注解即可
 * >> 如果同一个方法需要在两个库查询数据，可以手动执行如下
 *                  1、setDataSourceType(String dsType) >> 切换数据源
 *                  2、执行查询                           >> 执行查询
 *                  3、clearDataSourceType()            >> 清空数据源类型
 * >> 【默认】不配置时，默认名使用的时defaultDataSource数据库
 *
 * @author chenwt
 * @version V1.0
 * @describe 【】
 * @date 2022年09月14日 12:06:56
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    public static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);

    /**
     * 使用ThreadLocal维护变量，ThreadLocal为每个使用该变量的线程提供独立的变量副本，
     * 所以每一个线程都可以独立地改变自己的副本，而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public DynamicDataSource(DataSource dataSource, Map<Object, Object> targetDataSources) {
        // 配置主数据源
        super.setDefaultTargetDataSource(dataSource);

        // 配置  配置数据源
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    /**
     * 获取当前执行的数据源
     * 在每次调用数据库查询之前，抽象方法 determineCurrentLookupKey() 决定使用那个数据源
     *
     * @return 当前执行sql的数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSource.getDataSourceType();
    }

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType) {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}
